/**
* Project: yui3-common-mybatisx
 * Class GenerateWrapper
 * Version 1.0
 * File Created at 2019年1月24日
 * $Id$
 * author yuyi
 * email 1060771195@qq.com
 */
package yui.comn.mybatisx.extension.conditions;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import yui.comn.mybatisx.core.conditions.query.FindWrapper;
import yui.comn.mybatisx.core.toolkit.CollStrUtils;
import yui.comn.mybatisx.extension.conditions.clauses.SelectClause;
import yui.comn.mybatisx.extension.conditions.clauses.JoinClause;
import yui.comn.mybatisx.extension.conditions.clauses.OrderClause;
import yui.comn.mybatisx.extension.conditions.clauses.PageClause;
import yui.comn.mybatisx.extension.conditions.clauses.QueryClause;
import yui.comn.mybatisx.extension.conditions.clauses.RuleClause;
import yui.comn.mybatisx.extension.conditions.clauses.WhereClause;

/**
 * <p>
 * 生成查询类
 * </p>
 * @author yuyi (1060771195@qq.com)
 */
@SuppressWarnings("all")
public class GenerateWrapper {

    public static void wrapperHandle(FindWrapper wrapper, QueryClause queryClause, 
            Map<String, String> queryMapper) {
        wrapperHandle(wrapper, queryClause, queryMapper, null);
   }
    
    public static void wrapperHandle(FindWrapper wrapper, QueryClause queryClause, 
            Map<String, String> queryMapper, Map<String, String> fieldMapper) {
        
        joinHandle(wrapper, queryMapper, queryClause.getJ());  // on处理
        
        whereHandle(wrapper, queryMapper, queryClause.getW());  // 简单处理
        ruleHandle(wrapper, queryMapper, queryClause.getR());   // 复杂处理
        
        orderHandle(wrapper, queryMapper, queryClause.getO());
        pageHandle(wrapper, queryMapper, queryClause.getP());
        
        fieldHandle(wrapper, fieldMapper, queryClause.getS());
    }
    
    public static void fieldHandle(FindWrapper<Object> wrapper, Map<String, String> fieldMapper, SelectClause fieldClause) {
        if (null != fieldMapper && null != fieldClause && StringUtils.checkValNotNull(fieldClause.getV())) {
            List<String> fields = CollStrUtils.toStrList(fieldClause.getV());
            List<String> columns = new ArrayList<>();
            for (String field : fields) {
                String mapper = fieldMapper.get(field);
                if (StringUtils.checkValNotNull(mapper)) {
                    columns.add(mapper);
                }
            }
            String[] select = columns.toArray(new String[columns.size()]);
            if (ArrayUtils.isNotEmpty(select)) {
                wrapper.select(select);
            }
        }
    }
    
    public static void pageHandle(FindWrapper<Object> wrapper, Map<String, String> queryMapper, PageClause pageClause) {
        if (null != pageClause) {
            boolean isSearchCount = 0 == pageClause.getC() ? true : false;
            wrapper.setPageNo(pageClause.getN());
            wrapper.setPageSize(pageClause.getS());
            wrapper.setSearchCount(isSearchCount);
        }
    }
    
    public static void orderHandle(FindWrapper<Object> wrapper, Map<String, String> queryMapper, List<OrderClause> orderList) {
        if (CollectionUtils.isNotEmpty(orderList)) {
            for (OrderClause orderClause : orderList) {
                String column = getColumn(queryMapper, orderClause.getK());
                SqlCompareMode mode = SqlCompareMode.get(orderClause.getT(), SqlCompareMode.ASC);
                if (mode == SqlCompareMode.ASC || mode == SqlCompareMode.AC) {
                    wrapper.orderByAsc(column);
                } else {
                    wrapper.orderByDesc(column);
                }
            }
        }
    }
    
    public static void joinHandle(FindWrapper<Object> wrapper, Map<String, String> queryMapper, List<JoinClause> joinList) {
        if (CollectionUtils.isNotEmpty(joinList)) {
            for (JoinClause joinClause : joinList) {
                FindWrapper<Object> onFw = new FindWrapper<>();
                onFw.getParamNameSeq().set(wrapper.getParamNameSeq().get());
                
                ruleHandle(onFw, queryMapper, joinClause.getR());
                
                wrapper.getParamNameSeq().set(onFw.getParamNameSeq().get());
                wrapper.getParamNameValuePairs().putAll(onFw.getParamNameValuePairs());
                wrapper.addSqlOn(joinClause.getN(), onFw.getSqlSegment());
            }
        }
    }
    
    public static void ruleHandle(FindWrapper<Object> wrapper, Map<String, String> queryMapper, List<RuleClause> ruleList) {
        if (CollectionUtils.isNotEmpty(ruleList)) {
            for (RuleClause ruleClause : ruleList) {
                Consumer<FindWrapper<Object>> func = new Consumer<FindWrapper<Object>>() {

                    @Override
                    public void accept(FindWrapper<Object> t) {
                        whereHandle(t, queryMapper, ruleClause.getW());
                    }
                    
                };
                if (SqlCompareMode.OR.symbol().equalsIgnoreCase(ruleClause.getT())
                        || SqlCompareMode.O.symbol().equalsIgnoreCase(ruleClause.getT())) {
                    // fw.or(a -> a.eq("column", "zhangsan"));
                    wrapper.or(func);
                } else {
                    wrapper.and(func);
                }
            }
        }
    }
    
    public static void whereHandle(FindWrapper<Object> wrapper, Map<String, String> queryMapper, List<WhereClause> whereList) {
        if (CollectionUtils.isNotEmpty(whereList)) {
            // 排序
            Collections.sort(whereList, new Comparator<WhereClause>() {
                @Override
                public int compare(WhereClause o1, WhereClause o2) {
                    if (o1.getS() >= o2.getS()) {
                        return 1;
                    }
                    return -1;
                }
            });
            
            for (WhereClause whereClause : whereList) {
                String keyStr = whereClause.getK();
                
                // 处理分隔符
                String separatorChars = StringPool.UNDERSCORE + StringPool.UNDERSCORE;
                if (keyStr.contains(StringPool.COMMA)) {
                    separatorChars = StringPool.COMMA;
                }
                
                // String[] keys = StringUtils.split(keyStr, separatorChars);
                String[] keys = keyStr.split(separatorChars);
                
                int ind = 0;
                for (String key : keys) {
                    String column = getColumn(queryMapper, key);
                    
                    wrapper.or(ind++ > 0
                            || SqlCompareMode.OR.symbol().equalsIgnoreCase(whereClause.getT())
                            || SqlCompareMode.O.symbol().equals(whereClause.getT()));
                    
                    whereHandle(wrapper, queryMapper, whereClause, column);
                }
            }
        }
    }
    
    private static void whereHandle(FindWrapper<Object> wrapper, Map<String, String> queryMapper, WhereClause whereClause, String column) {
        SqlCompareMode mode = SqlCompareMode.get(whereClause.getM(), SqlCompareMode.LK);
        Object val = whereClause.getV();
        // 数值类型不允许用模糊查询(LIKE查询)，SqlCompareMode传过来是LK自动转EQ，如果想模糊查询，传过来的时候用字符串类型(加上引号)
        if (val instanceof Double && mode.name().equals(SqlCompareMode.LK.name())) {
            mode = SqlCompareMode.EQ;
        }
        
        switch (mode) {
            case EQ: 
                wrapper.eq(column, val);
                break;
            case NE: 
                wrapper.ne(column, val);
                break;
            case LT: 
                wrapper.lt(column, val);
                break;
            case LE: 
                wrapper.le(column, val);
                break;
            case GT: 
                wrapper.gt(column, val);
                break;
            case GE:
                wrapper.ge(column, val);
                break;
            case LK: 
                wrapper.like(column, val);
                break;
            case LLK:
                wrapper.likeLeft(column, val);
                break;
            case RLK:
                wrapper.likeRight(column, val);
                break;
            case NLK:
                wrapper.notLike(column, val);
                break;
            case IN: 
                if (val instanceof Collection) {
                    wrapper.in(column,  (Collection) val);
                } else {
                    // wrapper.in(column, trimArrayElements(StringUtils.split(String.valueOf(val), StringPool.COMMA)));
                    wrapper.in(column, trimArrayElements(String.valueOf(val).split(StringPool.COMMA)));
                }
                break;
            case NIN:
                if (val instanceof Collection) {
                    wrapper.notIn(column,  val);
                } else {
                    // wrapper.notIn(column, trimArrayElements(StringUtils.split(String.valueOf(val), StringPool.COMMA)));
                    wrapper.notIn(column, trimArrayElements(String.valueOf(val).split(StringPool.COMMA)));
                }
                break;
            case IS: 
                wrapper.isNull(column);
                break;
            case NIS:
                wrapper.isNotNull(column);
                break;
            case BT: 
                if (val instanceof Collection) {
                    List<String> valArr = (List<String>) val;
                    wrapper.between(column, getStartDateTime(valArr.get(0)), getEndDateTime(valArr.get(1)));
                } else {
                    // String[] vals = trimArrayElements(StringUtils.split(String.valueOf(val), StringPool.COMMA));
                    String[] vals = trimArrayElements(String.valueOf(val).split(StringPool.COMMA));
                    if (vals.length != 2) {
                        throw new IllegalArgumentException("between mode param value length not equal 2");
                    }
                    wrapper.between(column, getStartDateTime(vals[0]), getEndDateTime(vals[1]));
                }
                break;
            case OLK:
        		wrapper.and(new Consumer<FindWrapper<Object>>() {
        			@Override
        			public void accept(FindWrapper<Object> t) {
        				List<String> tList = getValList(val);
        				for (String tv : tList) {
        					t.like(column, tv);
        					t.or();
        				}
        			}
        		});
                break;
            case OLLK:
        		wrapper.and(new Consumer<FindWrapper<Object>>() {
        			@Override
        			public void accept(FindWrapper<Object> t) {
        				List<String> tList = getValList(val);
        				for (String tv : tList) {
        					t.likeLeft(column, tv);
        					t.or();
        				}
        			}
        		});
                break;
            case ORLK:
        		wrapper.and(new Consumer<FindWrapper<Object>>() {
        			@Override
        			public void accept(FindWrapper<Object> t) {
        				List<String> tList = getValList(val);
        				for (String tv : tList) {
        					t.likeRight(column, tv);
        					t.or();
        				}
        			}
        		});
                break;
            case ONLK:
            	wrapper.and(new Consumer<FindWrapper<Object>>() {
        			@Override
        			public void accept(FindWrapper<Object> t) {
        				List<String> tList = getValList(val);
        				for (String tv : tList) {
        					t.notLike(column, tv);
        					t.or();
        				}
        			}
        		});
                break;
            default:
                wrapper.like(column, val);
                break;
        }
    }
    
    private static List<String> getValList(Object val) {
    	List<String> tList = null;
		if (val instanceof Collection) {
			tList = (List<String>) val;
		} else {
			tList = CollStrUtils.toStrList(val);
		}
		return tList;
    }
    
    private static String getColumn(Map<String, String> queryMapper, String key) {
        String column = null;
        
        if (null != queryMapper) {
            column = queryMapper.get(key.trim());
        }
        
        if (StringUtils.isBlank(column)) {
            throw new RuntimeException("key:" + key + ", not find column");
        }
        
        return column;
    }
    
    private static String[] trimArrayElements(String[] array) {
        if (array == null || array.length == 0) {
            return new String[0];
        }

        String[] result = new String[array.length];
        for (int i = 0; i < array.length; i++) {
            String element = array[i];
            result[i] = (element != null ? element.trim() : null);
        }
        return result;
    }
    
    private static String getStartDateTime(String dateTime) {
        if (!dateTime.contains(":")) {
            return dateTime + " 00:00:00";
        }
        return dateTime;
    }
    
    private static String getEndDateTime(String dateTime) {
        if (!dateTime.contains(":")) {
            return dateTime + " 23:59:59";
        }
        return dateTime;
    }
    
    public static void main(String[] args) {
        int ind = 0;
        for (int i = 0; i < 10; i++) {
            System.out.println("a:" + i);
            System.out.println("b:" +ind);
            if (ind++ > 0) {
                System.out.println("c:" +ind);
            }
        }
                
    }
}
